package com.xxx.mch.feign;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import com.xxx.mch.config.MchInterepter;
import com.xxx.mch.exception.BusinessException;
import feign.*;
import feign.codec.ErrorDecoder;
import feign.hystrix.HystrixFeign;
import feign.hystrix.SetterFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.lang.reflect.Method;

@Configuration
@Slf4j
public class FeignConfiguration implements RequestInterceptor {

      @Value("${hive.site-code}")
      private String siteCode;

      @Value("${hive.system-code}")
      private String systemCode;

      @Override
      public void apply(RequestTemplate template) {
            template.header("sobeyhive-http-token", MchInterepter.globalToken);
            template.header("sobeyhive-http-system", systemCode);
            template.header("sobeyhive-http-site", siteCode);
      }

      @Bean
      Logger.Level feignLoggerLevel() {
            return Logger.Level.FULL;
      }

      @Bean
      public IRule myRule() {
            return new RoundRobinRule(); // 显式的指定使用轮询算法
      }

      @Bean
      public ErrorDecoder errorDecoder() {
            return new SpecialErrorDecoder();
      }

      /**
       * 自定义错误
       */
      public class SpecialErrorDecoder implements ErrorDecoder {
            @Override
            public Exception decode(String methodKey, Response response) {
                  Exception exception = null;
                  try {
                        String json = Util.toString(response.body().asReader());
                        exception = new BusinessException(json);
                  } catch (IOException ex) {
                        log.error(ex.getMessage(), ex);
                  }
                  return exception;
            }
      }

      @Bean
      public Feign.Builder feignHystrixBuilder() {
            return HystrixFeign.builder().setterFactory(new SetterFactory() {
                  @Override
                  public HystrixCommand.Setter create(Target<?> target, Method method) {
                        return HystrixCommand.Setter
                                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("ts"))// 控制 RemoteProductService 下,所有方法的Hystrix Configuration
                                //线程池配置
                                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(200))      //并发执行的最大线程数，默认10
                                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withMaxQueueSize(1000)) //BlockingQueue的最大队列数，默认值-1
                                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(800))     //即使maxQueueSize没有达到，达到queueSizeRejectionThreshold该值后，请求也会被拒绝，默认值5
                                //配置资源隔离策略,使用信号量
                                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                                        //如果通过 semaphore 调用的时候，底层的网络调用延迟很严重，那么是无法 timeout 的，只能一直 block 住。一旦请求数量超过了 semephore 限定的数量之后，就会立即开启限流。
                                       //无法进行超时控制和异步化执行， 因为信号量资源隔离策略无法指定命令在特定的线程执行命令执行的线程将由rx控制， Hystrix 元法在命令执行超时后获取到对应的线程进行强制中断。
                                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                                        .withExecutionIsolationSemaphoreMaxConcurrentRequests(200) //设置信号量最大的并发请求数
                                        .withFallbackIsolationSemaphoreMaxConcurrentRequests(100) // 设置fallback最大请求并发数
                                        .withExecutionTimeoutInMilliseconds(1000)   // 超时配置。semaphore无效

                                        .withCircuitBreakerEnabled(true)//是否启用熔断器，默认是TURE。
                                        .withCircuitBreakerForceOpen(false)//熔断器强制打开，始终保持打开状态。默认值FLASE。
                                        .withCircuitBreakerForceClosed(false)//熔断器强制关闭，始终保持关闭状态。默认值FLASE。
                                        .withCircuitBreakerErrorThresholdPercentage(30)//设定错误百分比，默认值50%
                                        //默认值20.意思是至少有20个请求才进行errorThresholdPercentage错误百分比计算。比如一段时间（10s）内有19个请求全部失败了。错误百分比是100%，但熔断器不会打开
                                        .withCircuitBreakerRequestVolumeThreshold(4)
                                        .withCircuitBreakerSleepWindowInMilliseconds(10000)//半开试探休眠时间，默认值5000ms
                                );
                  }
            });
      }
}